<!DOCTYPE html> <!-- -*- html -*- -->
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1, maximum-scale=1">
  <title>Orthogonal Projection</title>
  <link rel="shortcut icon" href="img/gatech.gif"/>

  
      <link rel="stylesheet" href="css/demo.css?vers=2759ff">
  

  <style>
      
  </style>

</head>
<body>
    

    
        <script src="js/demo.js?vers=77646a"></script>
    

    <script type="text/javascript">
        "use strict";
        DomReady.ready(function() {

        var complementColor, constrainToW, decompPerp, decompProj, dot, labels, mode, norm1, norm2, numVecs, projColor, range, ref, ref1, ref2, showBasis, showComplement, showDecomp, showDistance, showGrid, showProj, showSummands, size, subName, summand1, summand2, summands, surfaceColor, vecLabel, vector, vector1, vector1Color, vector2, vector2Color, vectorColor, vectors;

range = urlParams.get('range', 'float', 10);

surfaceColor = new Color("violet");

complementColor = new Color("green");

vectorColor = new Color(0, 0, 0);

vector1Color = new Color("orange");

vector2Color = new Color("brown");

projColor = new Color("red");

vector1 = urlParams.get('u1', 'float[]', [1, 0, 0]);

vector2 = urlParams.get('u2', 'float[]', [0, 1, 0]);

vector = urlParams.get('vec', 'float[]', [1, 1, 0]);

numVecs = 2;

if ((urlParams.u1 != null) && (urlParams.u2 == null)) {
  numVecs = 1;
}

size = vector1.length;

if (vector1[2] == null) {
  vector1[2] = 0;
}

if (vector2[2] == null) {
  vector2[2] = 0;
}

vectors = [vector1, vector2].slice(0, numVecs);

decompProj = [1, 0, 0];

decompPerp = [0, 1, 0];

summand1 = [0, 0, 0];

summand2 = [0, 0, 0];

summands = [summand1, summand2].slice(0, numVecs);

labels = urlParams.get('labels', 'str[]', ['u1', 'u2']);

labels = labels.slice(0, numVecs);

vecLabel = (ref = urlParams.vecLabel) != null ? ref : 'x';

subName = (ref1 = urlParams.subname) != null ? ref1 : (numVecs === 2 ? 'W' : 'L');

dot = function(vec1, vec2) {
  return vec1[0] * vec2[0] + vec1[1] * vec2[1] + vec1[2] * vec2[2];
};

norm1 = dot(vector1, vector1);

norm2 = dot(vector2, vector2);

mode = (ref2 = urlParams.mode) != null ? ref2 : 'full';

switch (mode) {
  case 'full':
    showBasis = true;
    showGrid = true;
    showSummands = numVecs > 1;
    showDecomp = true;
    showProj = false;
    showDistance = false;
    showComplement = true;
    constrainToW = false;
    break;
  case 'decomp':
    showBasis = false;
    showGrid = false;
    showSummands = false;
    showDecomp = true;
    showProj = false;
    showDistance = false;
    showComplement = true;
    constrainToW = false;
    break;
  case 'basis':
    showBasis = true;
    showGrid = true;
    showSummands = numVecs > 1;
    showDecomp = false;
    showProj = false;
    showDistance = false;
    showComplement = false;
    constrainToW = true;
    break;
  case 'badbasis':
    showBasis = true;
    showGrid = true;
    showSummands = numVecs > 1;
    showDecomp = false;
    showProj = true;
    showDistance = false;
    showComplement = false;
    constrainToW = true;
    break;
  case 'distance':
    showBasis = false;
    showGrid = false;
    showSummands = false;
    showDecomp = false;
    showProj = false;
    showDistance = true;
    showComplement = true;
    constrainToW = false;
}

showBasis = urlParams.get('basis', 'bool', showBasis);

showGrid = urlParams.get('grid', 'bool', showGrid);

showSummands = urlParams.get('summands', 'bool', showSummands);

showDecomp = urlParams.get('decomp', 'bool', showDecomp);

showDistance = urlParams.get('distance', 'bool', showDistance);

showComplement = urlParams.get('complement', 'bool', showComplement);

constrainToW = urlParams.get('constrain', 'bool', constrainToW);

window.demo = new (size === 2 ? Demo2D : Demo)({}, function() {
  var basisElt, clipCube, coeffs, comboElt, complement, computeOut, decompElt, decompLabels, distElt, gui, hexColorBasis1, hexColorBasis2, hexColorPerp, hexColorProj, hexColorSummand1, hexColorSummand2, params, snap, subspace, sumElt, tmpVec, updateCaptions, view;
  window.mathbox = this.mathbox;
  params = {
    'Show Basis': showBasis,
    'Show Grid': showGrid,
    'Show Decomp': showDecomp,
    'Show Distance': showDistance,
    'Show Summands': showSummands
  };
  gui = new dat.GUI();
  gui.closed = this.urlParams.closed != null;
  gui.add(params, 'Show Basis').onFinishChange(function(val) {
    showBasis = val;
    return mathbox.select('.basis').set('visible', val);
  });
  gui.add(params, 'Show Grid').onFinishChange(function(val) {
    showBasis = val;
    return mathbox.select('#vecgrid').set('visible', val);
  });
  gui.add(params, 'Show Decomp').onFinishChange(function(val) {
    showDecomp = val;
    return mathbox.select('.decomp').set('visible', val);
  });
  gui.add(params, 'Show Distance').onFinishChange(function(val) {
    showDistance = val;
    return mathbox.select('.distance').set('visible', val);
  });
  gui.add(params, 'Show Summands').onFinishChange(function(val) {
    showSummands = val;
    return mathbox.select('.summands').set('visible', val);
  });
  view = this.view({
    axes: false,
    grid: false,
    viewRange: [[-range, range], [-range, range], [-range, range]].slice(0, size)
  });
  clipCube = this.clipCube(view, {
    draw: true,
    hilite: size === 3
  });
  subspace = this.subspace({
    vectors: vectors,
    live: false,
    noPlane: size === 2,
    color: surfaceColor,
    lineOpts: {
      zOrder: 0
    },
    surfaceOpts: {
      zOrder: 1
    }
  });
  subspace.draw(clipCube.clipped);
  this.grid(clipCube.clipped, {
    vectors: vectors,
    live: false,
    lineOpts: {
      color: surfaceColor
    }
  });
  mathbox.select('#vecgrid').set('visible', showGrid);
  if (showComplement) {
    complement = this.subspace({
      vectors: subspace.complementFull(size === 2),
      name: 'complement',
      color: complementColor,
      noPlane: size === 2,
      live: false,
      lineOpts: {
        zOrder: 0
      },
      surfaceOpts: {
        zOrder: 1
      }
    });
    complement.draw(clipCube.clipped);
  }
  this.labeledVectors(view, {
    vectors: [vector],
    colors: [vectorColor],
    labels: [vecLabel],
    live: true,
    vectorOpts: {
      zIndex: 4
    },
    labelOpts: {
      zIndex: 5
    }
  });
  this.labeledVectors(view, {
    name: 'basis',
    vectors: vectors,
    colors: [vector1Color.arr(.7), vector2Color.arr(.7)].slice(0, numVecs),
    labels: labels,
    live: false,
    vectorOpts: {
      zIndex: 2
    },
    labelOpts: {
      zIndex: 3
    }
  });
  this.labeledVectors(view, {
    name: 'summands',
    vectors: summands,
    colors: [vector1Color.brighten(.3).arr(.9), vector2Color.brighten(.3).arr(.9)].slice(0, numVecs),
    labels: null,
    live: true,
    vectorOpts: {
      zIndex: 3
    }
  });
  this.labeledVectors(view, {
    name: 'decomp',
    vectors: [decompProj, vector],
    origins: [[0, 0, 0], decompProj],
    colors: [surfaceColor.darken(.1), complementColor.darken(.1)],
    labels: [vecLabel + "_" + subName, vecLabel + "_" + subName + "\u27C2"],
    live: true,
    vectorOpts: {
      zIndex: 4
    },
    labelOpts: {
      zIndex: 5,
      outline: 1,
      background: "white"
    }
  });
  if (showProj) {
    this.labeledVectors(view, {
      name: 'proj',
      vectors: [decompProj],
      colors: [projColor],
      labels: ['?'],
      live: true,
      vectorOpts: {
        zIndex: 4
      },
      labelOpts: {
        zIndex: 5
      }
    });
    view.array({
      width: 2,
      channels: 3,
      items: 4,
      data: [[summands[0], summands[1], summands[0], summands[1]], [vector, vector, decompProj, decompProj]]
    }).array({
      width: 2,
      channels: 4,
      items: 4,
      data: [[[1, 1, 1, .7], [1, 1, 1, .7], [0.2157, 0.4941, 0.7216, .7], [0.3020, 0.6863, 0.2902, .7]], [[1, 1, 1, .7], [1, 1, 1, .7], [0.2157, 0.4941, 0.7216, .7], [0.3020, 0.6863, 0.2902, .7]]]
    }).line({
      width: 2,
      points: "<<",
      color: "black",
      colors: "<"
    });
  }
  decompLabels = ['x_W', "1.0"];
  this.labeledVectors(view, {
    name: 'distance',
    vectors: [decompProj, vector],
    origins: [[0, 0, 0], decompProj],
    colors: [surfaceColor.darken(.1), complementColor.darken(.1)],
    labels: decompLabels,
    live: true,
    labelsLive: true,
    vectorOpts: {
      zIndex: 4,
      end: false
    },
    labelOpts: {
      zIndex: 5,
      outline: 1,
      background: "white"
    }
  });
  view.array({
    width: 1,
    channels: 3,
    data: [decompProj]
  }).point({
    size: 15,
    color: surfaceColor.darken(.1).arr(),
    classes: ['distance']
  }).text({
    live: false,
    width: 1,
    data: ['closest']
  }).label({
    classes: ['distance'],
    color: surfaceColor.darken(.1).arr(),
    outline: 0,
    background: "black",
    size: 15,
    offset: [0, -25]
  });
  mathbox.select('.basis').set('visible', showBasis);
  mathbox.select('.decomp').set('visible', showDecomp);
  mathbox.select('.distance').set('visible', showDistance);
  mathbox.select('.summands').set('visible', showSummands);
  snap = (function(_this) {
    return function(vec) {
      if (constrainToW) {
        return subspace.project(vec, vec);
      }
    };
  })(this);
  coeffs = [0, 0];
  computeOut = function() {
    var dot1, dot2;
    dot1 = dot(vector, vector1);
    coeffs[0] = dot1 / norm1;
    decompProj[0] = vector1[0] * coeffs[0];
    decompProj[1] = vector1[1] * coeffs[0];
    decompProj[2] = vector1[2] * coeffs[0];
    summand1[0] = decompProj[0];
    summand1[1] = decompProj[1];
    summand1[2] = decompProj[2];
    if (numVecs > 1) {
      dot2 = dot(vector, vector2);
      coeffs[1] = dot2 / norm2;
      summand2[0] = vector2[0] * coeffs[1];
      summand2[1] = vector2[1] * coeffs[1];
      summand2[2] = vector2[2] * coeffs[1];
      decompProj[0] += summand2[0];
      decompProj[1] += summand2[1];
      decompProj[2] += summand2[2];
    }
    decompPerp[0] = vector[0] - decompProj[0];
    decompPerp[1] = vector[1] - decompProj[1];
    decompPerp[2] = vector[2] - decompProj[2];
    decompLabels[1] = Math.sqrt(dot(decompPerp, decompPerp)).toFixed(2);
    return updateCaptions();
  };
  this.draggable(view, {
    points: [vector],
    onDrag: snap,
    postDrag: computeOut
  });
  hexColorProj = surfaceColor.str();
  hexColorPerp = complementColor.str();
  hexColorBasis1 = vector1Color.str();
  hexColorBasis2 = vector2Color.str();
  hexColorSummand1 = vector1Color.brighten(.1).str();
  hexColorSummand2 = vector2Color.brighten(.1).str();
  switch (mode) {
    case 'full':
      this.caption('<p><span id="sum-here"></span></p>');
      sumElt = document.getElementById('sum-here');
      updateCaptions = (function(_this) {
        return function() {
          var str;
          str = "\\color{" + hexColorProj + "}{" + vecLabel + "_{" + subName + "}} = ";
          str += _this.texVector(decompProj, {
            color: hexColorProj
          });
          str += '=';
          str += _this.texCombo(vectors, coeffs, {
            colors: [hexColorBasis1, hexColorBasis2].slice(0, numVecs),
            coeffColors: [hexColorSummand1, hexColorSummand2].slice(0, numVecs)
          });
          return katex.render(str, sumElt);
        };
      })(this);
      break;
    case 'decomp':
      this.caption('<p><span id="decomp-here"></span></p>');
      decompElt = document.getElementById('decomp-here');
      updateCaptions = (function(_this) {
        return function() {
          var str;
          str = _this.texVector(vector);
          str += '=';
          str += _this.texVector(decompProj, {
            color: hexColorProj
          });
          str += '+';
          str += _this.texVector(decompPerp, {
            color: hexColorPerp
          });
          return katex.render(str, decompElt);
        };
      })(this);
      break;
    case 'distance':
      this.caption('<p><span id="sum-here"></span></p>' + '<p><span id="dist-here"></span></p>');
      sumElt = document.getElementById('sum-here');
      distElt = document.getElementById('dist-here');
      updateCaptions = (function(_this) {
        return function() {
          var str;
          str = "\\color{" + hexColorProj + "}{" + vecLabel + "_{" + subName + "}} = ";
          str += _this.texVector(decompProj, {
            color: hexColorProj
          });
          str += '=';
          str += _this.texCombo(vectors, coeffs, {
            colors: [hexColorBasis1, hexColorBasis2].slice(0, numVecs),
            coeffColors: [hexColorSummand1, hexColorSummand2].slice(0, numVecs)
          });
          katex.render(str, sumElt);
          str = "\\|\\color{" + hexColorPerp + "}{x_{W^\\perp}}\\| =";
          str += "\\left\\|";
          str += _this.texVector(vector);
          str += '-';
          str += _this.texVector(decompProj, {
            color: hexColorProj
          });
          str += "\\right\\|";
          str += '=' + decompLabels[1];
          return katex.render(str, distElt);
        };
      })(this);
      break;
    case 'basis':
      this.caption('<p><span id="combo-here"></span></p>' + '<p><span id="basis-here"></span></p>');
      comboElt = document.getElementById('combo-here');
      basisElt = document.getElementById('basis-here');
      updateCaptions = (function(_this) {
        return function() {
          var str;
          str = "x = \\text{proj}_{" + subName + "}(" + vecLabel + ") =";
          str += _this.texVector(vector);
          str += '=';
          str += _this.texCombo(vectors, coeffs, {
            colors: [hexColorBasis1, hexColorBasis2].slice(0, numVecs),
            coeffColors: [hexColorSummand1, hexColorSummand2].slice(0, numVecs)
          });
          katex.render(str, comboElt);
          str = '[x]_{\\mathcal B} = ';
          str += '\\big(';
          str += ("\\color{" + hexColorSummand1 + "}{" + (coeffs[0].toFixed(2)) + "},\\;") + ("\\color{" + hexColorSummand2 + "}{" + (coeffs[1].toFixed(2)) + "}");
          str += '\\big)';
          return katex.render(str, basisElt);
        };
      })(this);
      break;
    case 'badbasis':
      this.caption('<p><span id="combo-here"></span></p>' + '<p><span id="basis-here"></span></p>');
      comboElt = document.getElementById('combo-here');
      updateCaptions = (function(_this) {
        return function() {
          var str;
          str = 'x = ';
          str += _this.texVector(vector);
          str += "\\neq";
          str += _this.texCombo(vectors, coeffs, {
            colors: [hexColorBasis1, hexColorBasis2].slice(0, numVecs),
            coeffColors: [hexColorSummand1, hexColorSummand2].slice(0, numVecs)
          });
          str += '=';
          str += _this.texVector(decompProj, {
            color: projColor
          });
          return katex.render(str, comboElt);
        };
      })(this);
  }
  if (constrainToW) {
    tmpVec = new THREE.Vector3(vector[0], vector[1], vector[2]);
    subspace.project(tmpVec, tmpVec);
    vector[0] = tmpVec.x;
    vector[1] = tmpVec.y;
    vector[2] = tmpVec.z;
  }
  return computeOut();
});


        });
    </script>
</body>
</html>

